#include "sceneobjectbase.h"
#include "../common.h"
#include "../player.h"
#include "../effect.h"
#include "../camera.h"
#include "../gun/gun.h"
#include "../gamescene.h"
#include "../freshpoint.h"

namespace fireplane{
	//////////////////////////////////////////////////////////////////////////
	class SceneObjectFadeInFadeOut : public SceneObjectBase{
	public:
		SceneObjectFadeInFadeOut();
		virtual ~SceneObjectFadeInFadeOut();
		virtual void init(string fileName,CCPoint pos,float angle,float s_x,float s_y);
	private:
	};

	SceneObjectFadeInFadeOut::SceneObjectFadeInFadeOut()
	{}

	SceneObjectFadeInFadeOut::~SceneObjectFadeInFadeOut()
	{}

	void SceneObjectFadeInFadeOut::init( string fileName,CCPoint pos,float angle,float s_x,float s_y )
	{
		SceneObjectBase::init(fileName,pos,angle,s_x,s_y);

		int r = rand() % 5;
		if (r < 0)
		{
			r = -r;
		}
		r += 1;

		float d = (float)r;
		d = d / 10;

		CCAction* fadeout = CCFadeOut::create(d);
		CCAction* fadein = CCFadeIn::create(d);

		CCAction* action = CCSequence::create((CCFiniteTimeAction*)fadeout,fadein,NULL);
		action = CCRepeatForever::create((CCActionInterval*)action);
		action->setTag(0x1234);

		_node->runAction(action);
	}

	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	class SceneObjectRotate : public SceneObjectBase{
	public:
		SceneObjectRotate();
		virtual ~SceneObjectRotate();
		virtual void init(string fileName,CCPoint pos,float angle,float s_x,float s_y);
	private:
	};

	SceneObjectRotate::SceneObjectRotate()
	{}

	SceneObjectRotate::~SceneObjectRotate()
	{}

	void SceneObjectRotate::init( string fileName,CCPoint pos,float angle,float s_x,float s_y )
	{
		SceneObjectBase::init(fileName,pos,angle,s_x,s_y);
		this->setContentSize(_node->getContentSize());
		_node->setAnchorPoint(ccp(65.0f / 134.0f,1.0f - 18.0f / 155.0f));
		CCPoint tpos = this->getPosition();
		tpos.y += 155.0f / 2 - 18.0f;
		this->setPosition(tpos);

		int r = rand() % 45;
		if(r < 0) r = -r;
		_node->setRotation(r);

		CCAction* r0 = CCRotateTo::create(2.5f,45.0f);
		CCAction* r1 = CCRotateTo::create(2.5f,-45.0f);

		CCAction* action = CCSequence::create((CCFiniteTimeAction*)r0,r1,NULL);
		action = CCRepeatForever::create((CCActionInterval*)action);
		action->setTag(0x1234);
		_node->runAction(action);
	}
	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	class SceneObjectOfBomb : public SceneObjectBase{
	public:
		SceneObjectOfBomb();
		virtual ~SceneObjectOfBomb();
		enum SBBState{
			SBB_wait = 0,
			SBB_move_2_collision
		};
		
		void init( string fileName,CCPoint pos,float angle,float s_x,float s_y );
		void update( float delta );
		virtual void onAttack(bool kill);
	private:
		SBBState _state;
		CCPoint _desPos;
	};

	SceneObjectOfBomb::SceneObjectOfBomb()
	{}

	SceneObjectOfBomb::~SceneObjectOfBomb()
	{}

	void SceneObjectOfBomb::update(float dt){
		this->retain();

		SceneObjectBase::update(dt);
		
		if(!_is_not_active){
			switch (_state){
			case SBB_wait:{
					Player* mainPlayer = Player::getMainPlayer();
					CCPoint playerPos = mainPlayer->getPlayerPos();

					CCPoint myPos = this->getPosition();
					myPos = this->getParent()->convertToWorldSpace(myPos);
					myPos = mainPlayer->getPlayerParent()->convertToNodeSpace(myPos);

					float dis = ccpDistance(myPos,playerPos);
					if(dis < 2 * 160){
						_state = SBB_move_2_collision;

						this->retain();
						this->getParent()->removeChild(this,false);
						mainPlayer->getPlayerParent()->addChild(this);
						this->release();
						this->setPosition(myPos);

						_desPos = ccpSub(playerPos,myPos);
						_desPos = ccpNormalize(_desPos);
					}
				}
				break;

			case SBB_move_2_collision:{
					CCPoint mypos = this->getPosition();
					CCPoint move;
					move.x = _desPos.x * 300.0f * dt;
					move.y = _desPos.y * 300.0f * dt;
					mypos.x += move.x;
					mypos.y += move.y;

					this->setPosition(mypos);

					mypos = this->getParent()->convertToWorldSpace(mypos);
					mypos.y += this->getContentSize().height;
					if (mypos.y < 0)
					{
						this->removeFromParentAndCleanup(true);
						removeFromActiveList(this);
						unscheduleUpdate();
					}
				}
				break;
			}
		}

		this->release();
	}

	void SceneObjectOfBomb::init( string fileName,CCPoint pos,float angle,float s_x,float s_y )
	{
		SceneObjectBase::init(fileName,pos,angle,s_x,s_y);

		CCActionInterval* action = 
			Common::createFrameAnimation("mapdata/level0/animation/bomb/cilizhandan",6,CCRectMake(0,0,32,30));

		action = CCRepeatForever::create(action);
		action->setTag(0x1234);
		_node->runAction(action);

		_state = SBB_wait;

		// retain for this list node
		add2AcitiveList(this);
	}

	void SceneObjectOfBomb::onAttack(bool kill)
	{
		CCPoint worldpos = this->getPosition();
		worldpos = this->getParent()->convertToWorldSpace(worldpos);

		Effect::CreateBomb0OrBomb1 cmd;
		cmd._id = Effect::ET_bomb1;
		cmd.worldPos = worldpos;
		Effect::createEffect(&cmd);

		_is_destroyed = true;
	}

	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	class SceneObjectStoneNormal : public SceneObjectBase{
	public:
		SceneObjectStoneNormal();
		virtual ~SceneObjectStoneNormal();

		void init( string fileName,CCPoint pos,float angle,float s_x,float s_y );
		void update( float delta );
		virtual void onAttack(bool kill);
		virtual CCRect getCollisionRect();
		virtual bool isCollision2Something(CCPoint pos);

		virtual bool isCollision2Player(Player* player);
		virtual void onCollision2Player();
	private:

		void _onAttack(int des);

		float _speed;
		float _hp;
		CollisionBox* _box;
	};

	SceneObjectStoneNormal::SceneObjectStoneNormal():
		_hp(20.0f),_box(NULL)
	{}

	SceneObjectStoneNormal::~SceneObjectStoneNormal()
	{}

	//! Tells us if the given point lies to the left, right, or on the line.
		/** \return 0 if the point is on the line
		<0 if to the left, or >0 if to the right. */
	int getPointOrientation(CCPoint& start,CCPoint& end,const CCPoint& point)
	{
		return ( (end.x - start.x) * (point.y - start.y) -
				(point.x - start.x) * (end.y - start.y) );
	}

	void SceneObjectStoneNormal::init( string fileName,CCPoint pos,float angle,float s_x,float s_y )
	{
		//////////////////////////////////////////////////////////////////////////
		SceneObjectBase::init(fileName,pos,angle,s_x,s_y);

		_node->setScale(1.0f);

		{
#if 0	
			CCRotateBy* rotate = CCRotateBy::create(20.0f,180.0f);
			CCActionInterval* action = CCRepeatForever::create(rotate);
			action->setTag(0x1234);

			_node->runAction(action);

			int speed = rand() % 200;
			_speed = (float)speed;
			_speed = _speed / 10.0f;


			_speed = 0;

#else
			_speed = 0.0f;	
#endif

		}

		if(_debugName.find("1_ZJ_11_1") != -1){
			_box = GameScene::getGameScene()->getCollisionBox("CollisionBox 0");
			_collisionType = SOCBT_BOX;

			_speed = 0.0f;
		}

		add2AcitiveList(this);
	}

	void SceneObjectStoneNormal::update( float delta )
	{
		this->retain();
		SceneObjectBase::update(delta);

		if (!isVisible())
		{
			this->release();
			return;
		}

		if(!_is_not_active){
			CCPoint pos = this->getPosition();
			pos.y -= _speed * delta;

			this->setPosition(pos);

//  			pos = this->getParent()->convertToWorldSpace(pos);
//  			if(pos.y <= 240){
//  				Camera::getCamera()->setMoveEnableFlag(false);
//  			}
		}

		this->release();
	}

	void SceneObjectStoneNormal::_onAttack(int des){
		_hp += des;
		if(_hp <= 0){
			_hp = 0;

			CCPoint worldpos = this->getPosition();
			worldpos = this->getParent()->convertToWorldSpace(worldpos);

			Effect::CreateBomb2 cmd;
			cmd._id = Effect::ET_bomb2;
			cmd.worldPos = worldpos;
			cmd.path = "effect/bomb2/";
			cmd.animationCount = 8;
			cmd.rect = CCRectMake(0,0,174,153);

			Effect::createEffect(&cmd);

			_is_destroyed = true;
		}
	}

	void SceneObjectStoneNormal::onAttack(bool kill)
	{
		if(kill)
			_onAttack(-_hp);
		else
			_onAttack(-1);
	}

	CCRect SceneObjectStoneNormal::getCollisionRect()
	{
		CCSize size = _node->getContentSize();
		size.width *= 0.8f;
		size.height *= 0.8f;

		CCPoint pos = this->getPosition();
		return CCRectMake(pos.x - size.width / 2,pos.y - size.height / 2,size.width,size.height);
	}

	bool SceneObjectStoneNormal::isCollision2Something( CCPoint pos )
	{
		if(!_box){
			return SceneObjectBase::isCollision2Something(pos);
		}
		else{
			CCPoint spos = pos;
			spos = _node->convertToNodeSpace(pos);

			CCSize size = _node->getContentSize();
			CCRect rect = CCRectMake(0,0,size.width,size.height);

			if (!rect.containsPoint(spos)){
				return false;
			}
			else{
				for (unsigned int i = 0;i < _box->_all_points.size();i ++)
				{
					int c = i;
					int n = i + 1;
					n = n % _box->_all_points.size();

					CCPoint start = _box->_all_points[c];
					CCPoint next = _box->_all_points[n];

					int o = getPointOrientation(start,next,spos);

					if(o < 0){
						return false;
					}
				}

				return true;
			}
		}
	}

	bool SceneObjectStoneNormal::isCollision2Player( Player* player )
	{
		if(!isVisible())
			return false;

		if(_box){
			CCRect player_rect = player->getPlayerWorldCollisionSize();

			CCAffineTransform tan1 =  _node->worldToNodeTransform();

			CCPoint pos[4];
			pos[0] = player_rect.origin;
			pos[0] = _node->convertToNodeSpace(pos[0]);

			pos[1] = player_rect.origin;
			pos[1].x += player_rect.size.width;pos[1] = _node->convertToNodeSpace(pos[1]);
			pos[2] = player_rect.origin;pos[2].x += player_rect.size.width;pos[2].y += player_rect.size.height;pos[2] = _node->convertToNodeSpace(pos[2]);
			pos[3] = player_rect.origin;pos[3].y += player_rect.size.height;pos[3] = _node->convertToNodeSpace(pos[3]);

			for (int p = 0;p < 4;p++)
			{
				bool ok = true;
				for (unsigned int i = 0;i < _box->_all_points.size();i ++)
				{
					int c = i;
					int n = i + 1;
					n = n % _box->_all_points.size();

					CCPoint start = _box->_all_points[c];
					CCPoint next = _box->_all_points[n];

					int o = getPointOrientation(start,next,pos[p]);

					if(o < 0){
						ok = false;
						break;
					}
				}

				if(ok){
					//CCLog("collision collision");
					return true;//return false;//
				}
			}

			return false;
		}
		else{
			return SceneObjectBase::isCollision2Player(player);
		}
	}

	void SceneObjectStoneNormal::onCollision2Player()
	{
		_onAttack(-_hp);
	}

	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	class SceneObjectTank : public SceneObjectBase{
	public:
		SceneObjectTank();
		virtual ~SceneObjectTank();

		void init( string fileName,CCPoint pos,float angle,float s_x,float s_y );
		void update( float delta );
		virtual void onAttack(bool kill);
		void rotate2player();
		void fireLogic(float delta);
	private:
		float _fire_delay;
		Gun* _gun;
		CCPoint _dir;

		float _hp;
	};

	SceneObjectTank::SceneObjectTank():
		_fire_delay(0)
	{}

	SceneObjectTank::~SceneObjectTank()
	{

	}

	void SceneObjectTank::init( string fileName,CCPoint pos,float angle,float s_x,float s_y )
	{
		SceneObjectBase::init(fileName,pos,angle,s_x,s_y);

		Gun::CreateNormalGun arg(Gun::GT_normal_gun);
		arg.tdir = ccp(0,-1);
		arg.tspeed = 200;
		arg.o = Bullet::BO_ENEMY;
		arg.pos = ccp(0,-10);

		_gun = Gun::createGun(&arg);
		addChild(_gun);
		add2AcitiveList(this);
		_hp = 20;
	}

	void SceneObjectTank::rotate2player(){
		CCPoint playerPos = Player::getMainPlayer()->getPlayerPos();
		CCPoint mypos = this->getPosition();
		mypos = this->getParent()->convertToWorldSpace(mypos);
		mypos = Player::getMainPlayer()->getPlayerParent()->convertToNodeSpace(mypos);

		_dir.x = playerPos.x - mypos.x;
		_dir.y = playerPos.y - mypos.y;

		_dir = ccpNormalize(_dir);

		CCPoint up(0,-1);
		float angle = ccpDot(_dir,up);
		angle = acos(angle);
		angle = angle * 180.0f/ 3.14f;
		angle = -angle;

		float s = ccpCross(_dir,up);
		if(s > 0){
			angle = 360 - angle;
		}

		this->setRotation(angle);
	}

	void SceneObjectTank::fireLogic(float delta){
		_fire_delay += delta;
		if (_fire_delay > 1.0f)
		{
			_fire_delay = 0;
			_gun->fire(Bullet::BI_img1);
		}
	}

	void SceneObjectTank::update( float delta )
	{
		this->retain();

		SceneObjectBase::update(delta);
		
		if (isVisible())
		{
			if(!_is_not_active){
				rotate2player();
				fireLogic(delta);
			}			
		}

		this->release();
	}

	void SceneObjectTank::onAttack(bool kill)
	{
		if(kill)
			_hp = 0;
		else
			_hp -= 1;
		if(_hp <= 0){
			CCPoint worldpos = this->getPosition();
			worldpos = this->getParent()->convertToWorldSpace(worldpos);

			Effect::CreateBomb0OrBomb1 cmd;
			cmd._id = Effect::ET_bomb1;
			cmd.worldPos = worldpos;
			Effect::createEffect(&cmd);

			_is_destroyed = true;
		}
	}

	//////////////////////////////////////////////////////////////////////////

	SceneObjectBase::SceneObjectBase():
		_node(NULL),_lastVisible(true),_is_not_active(false), _is_destroyed(false)
	{}

	SceneObjectBase::~SceneObjectBase()
	{}

	SceneObjectBase* SceneObjectBase::create( string filename,CCPoint pos,float angle,float s_x,float s_y )
	{
		bool find = false;
		SceneObjectBase* ret = NULL;
		if(filename.find("1_ZJ_2") != -1 
			|| filename.find("1_ZJ_7") != -1
			|| filename.find("1_ZJ_9") != -1){
			ret = new SceneObjectFadeInFadeOut();
			ret->init(filename,pos,angle,s_x,s_y);
			ret->autorelease();

			find = true;
		}
		else if(filename.find("1_ZJ_10") != -1){
			ret = new SceneObjectRotate();
			ret->init(filename,pos,angle,s_x,s_y);
			ret->autorelease();

			find = true;
		}
		else if(filename.find("1_ZJ2_10000") != -1){
			ret = new SceneObjectOfBomb();
			ret->init(filename,pos,angle,s_x,s_y);
			ret->autorelease();
			find = true;
		}
		else if(filename.find("1_ZJ2_2") != -1
			|| filename.find("1_ZJ_11") != -1
			|| filename.find("1_ZJ2_3") != -1){
			ret = new SceneObjectStoneNormal();
			ret->init(filename,pos,angle,s_x,s_y);
			ret->autorelease();
			find = true;
		}
		else if(filename.find("1_ZJ2_4_1") != -1){
			ret = new SceneObjectTank();
			ret->init(filename,pos,angle,s_x,s_y);
			ret->autorelease();
			find = true;
		}

		if(!find){
			ret = new SceneObjectBase();
			ret->init(filename,pos,angle,s_x,s_y);
			ret->autorelease();
			
			//Check FreshPoint
			{
				int end = filename.length();
				int start = filename.find_last_of('/',end)+1;
				int dot = filename.find_last_of('.',end);
				string extName = filename.substr(dot,4);

				if ((end - start) == strlen("xxx.jpg")
					&& !extName.compare(".jpg"))
				{
					string fp_id = filename.substr(start,3);
					int id = atoi(fp_id.c_str());
					FreshPointBase::InitFreshPoint(id,pos);
				}
			}
		}

		return ret;
	}

	void SceneObjectBase::init( string fileName,CCPoint pos,float angle,float s_x,float s_y )
	{
		_debugName = fileName;

		_node = CCSprite::create(fileName.c_str());
		
		_node->setRotation(angle);

		if(s_x == -1.0f){
			_node->setFlipX(true);
		}
		else{
			_node->setScaleX(s_x);
		}

		if(s_y == -1.0f){
			_node->setFlipY(true);
		}
		else{
			_node->setScaleY(s_y);
		}

		addChild(_node);
		scheduleUpdate();

		/*this->setContentSize(_node->getContentSize());*/
		this->setPosition(pos);

		_collisionType = SOCBT_RECT;
	}

	void SceneObjectBase::update( float delta )
	{
		if(_is_destroyed){
			removeFromParentAndCleanup(true);
			removeFromActiveList(this);
			unscheduleUpdate();

			_is_not_active = true;
			return;
		}

#if 0
		CCPoint pos = this->getPosition();
		pos = this->getParent()->convertToWorldSpace(pos);

		CCSize size = _node->getContentSize();
		CCAffineTransform& tans = _node->nodeToWorldTransform();
		size = CCSizeApplyAffineTransform(size,tans);

		pos.x -= size.width / 2;
		pos.y -= size.height / 2;
		CCRect rect = CCRectMake(pos.x,pos.y,size.width,size.height);

		CCSize winsize = CCDirector::sharedDirector()->getWinSize();
		CCRect winrect = CCRectMake(0,0,winsize.width,winsize.height);
		bool ret = rect.intersectsRect(winrect);
#else
		CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
		CCRect windowRect = CCRectMake(0,0,winSize.width,winSize.height);

		CCAffineTransform& tans = _node->worldToNodeTransform();
		windowRect = CCRectApplyAffineTransform(windowRect,tans);

		CCSize content = _node->getContentSize();
		CCRect localRect = CCRectMake(0,0,content.width,content.height);
		bool ret = localRect.intersectsRect(windowRect);
		bool active = localRect.getMaxY() > windowRect.getMinY();
#endif

		if (ret){
			setVisible(true);
			if(_lastVisible != true){
				onVisible(true);
			}
		}
		else{
			setVisible(false);
			if(_lastVisible != false){
				onVisible(false);
			}
		}

		_lastVisible = ret;

		if(!active/*pos.y + size.height < 0*/){
			_is_not_active = true;

			removeFromActiveList(this);
			this->removeAllChildrenWithCleanup(true);
			unscheduleUpdate();
		}
	}

	void SceneObjectBase::onVisible( bool flag )
	{
		CCAction* action = _node->getActionByTag(0x1234);
		if(NULL == action)
			return;

		if (flag)
		{
			CCDirector::sharedDirector()->getActionManager()->resumeTarget(_node);
		}
		else{
			CCDirector::sharedDirector()->getActionManager()->pauseTarget(_node);
		}
	}

	void SceneObjectBase::add2AcitiveList(SceneObjectBase* object){
		object->retain();
		_all_active_sceneobject.push_back(object);
	}

	void SceneObjectBase::removeFromActiveList(SceneObjectBase* object){
		list<SceneObjectBase*>::iterator it = _all_active_sceneobject.begin();
		for (;it != _all_active_sceneobject.end();)
		{
			SceneObjectBase* tobject = (*it);
			if(tobject == object){
				it = _all_active_sceneobject.erase(it);
				tobject->release();
			}
			else{
				it ++;
			}
		}
	}

	list<SceneObjectBase*>& SceneObjectBase::getAllActiveSceneObject()
	{
		return _all_active_sceneobject;
	}

	CCRect SceneObjectBase::getCollisionRect()
	{
		CCSize size =  _node->getContentSize();	
		CCPoint pos = this->getPosition();
		return CCRectMake(pos.x - size.width / 2,pos.y - size.height / 2,size.width,size.height);
	}

	CCNode* SceneObjectBase::getSceneObjectParent()
	{
		return this->getParent();
	}

	void SceneObjectBase::onAttack(bool kill)
	{

	}

	bool SceneObjectBase::isCollision2Something( CCPoint worldPos )
	{
		CCPoint spos = worldPos;
		spos = _node->convertToNodeSpace(worldPos);

		CCSize size = _node->getContentSize();
		CCRect rect = CCRectMake(0,0,size.width,size.height);

		if (!rect.containsPoint(spos)){
			return false;
		}

		return true;
	}

	bool SceneObjectBase::isCollision2Player( Player* player )
	{
		if(!isVisible())
			return false;

		CCRect player_rect = player->getPlayerWorldCollisionSize();

		CCAffineTransform tan = this->worldToNodeTransform();
		player_rect.size = CCSizeApplyAffineTransform(player_rect.size,tan);
		player_rect.origin = this->getParent()->convertToNodeSpace(player_rect.origin);

		CCSize mysize = _node->getContentSize();
		CCPoint pos = this->getPosition();

		CCRect rect = CCRectMake(pos.x - mysize.width / 2,pos.y - mysize.height / 2,
			mysize.width,mysize.height);

		if(rect.intersectsRect(player_rect)){
			return true;
		}

		return false;
	}

	void SceneObjectBase::onCollision2Player()
	{
		onAttack(true);
	}

	list<SceneObjectBase*> SceneObjectBase::_all_active_sceneobject;


	BgObject::BgObject()
	{

	}

	BgObject::~BgObject()
	{

	}

	void BgObject::update( float delta )
	{
		CCPoint pos = _node->getPosition();
		pos = _node->getParent()->convertToWorldSpace(pos);

		CCSize size = _node->getContentSize();
		CCAffineTransform& tans = _node->getParent()->nodeToWorldTransform();
		size = CCSizeApplyAffineTransform(size,tans);

		CCRect rect = CCRectMake(pos.x,pos.y,size.width,size.height);
		CCSize winsize = CCDirector::sharedDirector()->getWinSize();
		CCRect winrect = CCRectMake(0,0,winsize.width,winsize.height);
		bool ret = rect.intersectsRect(winrect);

		if (ret){
			setVisible(true);
		}
		else{
			setVisible(false);
		}

		if(pos.y + size.height < 0){
			removeFromParentAndCleanup(true);
			unscheduleUpdate();
		}
	}

	BgObject* BgObject::create( string filename,CCPoint pos)
	{
		BgObject* ret = new BgObject();
		ret->init(filename,pos);
		ret->autorelease();

		return ret;
	}

	void BgObject::init( string fileName,CCPoint pos )
	{
		_node = CCSprite::create(fileName.c_str());
		_node->setAnchorPoint(ccp(0,0));
		this->setPosition(pos);

		addChild(_node);

		scheduleUpdate();
	}

	const CCSize& BgObject::getContentSize()
	{
		return _node->getContentSize();
	}

}